#include "MainWindow.h"
#include "Exception.h"

#define WINDOW_CLASSNAME    "Worm Roaster"  // Window class name

#undef UNICODE
#undef _UNICODE

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <sstream>
#include <sdl/sdl.h>
#include <sdl/SDL_opengl.h>
#include <sdl/SDL_image.h>

#include "a2.hpp"
#include "algebra.hpp"
#include "game_engine.hpp"
//#include "image.hpp"
#include "perlin.h"

static int tid = 1;

static int win_size = 600;

/*  Create checkerboard texture  */
#define NOISE_SIZE 256
#define checkImageWidth NOISE_SIZE
#define checkImageHeight NOISE_SIZE

static GLubyte checkImage[checkImageHeight][checkImageWidth][4];

#define barkwidth 395
#define barkheight 197
static GLubyte barkImage[barkheight][barkwidth][4];

static GLuint texName[3];

double deg_to_rad(double d)
{
  return M_PI*d/180;
}

double rad_to_deg(double r)
{
  return 180.0/M_PI*r;
}

void vertp(Point3D p)
{
  glVertex3d(p.x, p.y, p.z);
}

void makeCheckImage(void)
{
   double test_d = 99;
   int i, j, c;
   setup(checkImageWidth, checkImageHeight);
   for (i = 0; i < checkImageHeight; i++) {
      for (j = 0; j < checkImageWidth; j++) {
         c = ((((i&0x8)==0)^((j&0x8))==0))*255;
         double p = PerlinNoise_2D(i, j, 0.25, 1);
         //p = perlinNoise[i + checkImageWidth*j];
         test_d = p;
         p = fabs(p);
         //p = min(1, p);
         c = int(255*p);
         checkImage[i][j][0] = (GLubyte) 100 + int(p*200);
         checkImage[i][j][1] = (GLubyte) 55 + int(p*100);
         checkImage[i][j][2] = (GLubyte) 0;
         checkImage[i][j][3] = (GLubyte) 255;
#if 0
         checkImage[i][j][0] = (GLubyte) int(p*255);
         checkImage[i][j][1] = (GLubyte) int(p*255);
         checkImage[i][j][2] = (GLubyte) int(p*255);
         checkImage[i][j][3] = (GLubyte) 255;
#endif
      }
   }
}

void init(void)
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel(GL_FLAT);
   glEnable(GL_DEPTH_TEST);
   
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

   makeCheckImage();
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   glGenTextures(3, &texName[0]);
   glBindTexture(GL_TEXTURE_2D, texName[0]);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 
                   GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
                   GL_NEAREST);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, 
                checkImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 
                checkImage);
                   
  // bump map
  glBindTexture(GL_TEXTURE_2D, texName[1]);
  
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  
 	//Create normalisation cube map
	glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, texName[2]);
//	GenerateNormalisationCubeMap();
	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
 
  // worm text

  GLuint texture;			// This is a handle to our texture object
  SDL_Surface *surface;	// This surface will tell us the details of the image
  GLenum texture_format;
  GLint  nOfColors;

  if ( (surface = IMG_Load("bark2.png")) ) { 

    // Check that the image's width is a power of 2
    if ( (surface->w & (surface->w - 1)) != 0 ) {
      printf("warning: image.bmp's width is not a power of 2\n");
    }

    // Also check if the height is a power of 2
    if ( (surface->h & (surface->h - 1)) != 0 ) {
      printf("warning: image.bmp's height is not a power of 2\n");
    }

    // get the number of channels in the SDL surface
    nOfColors = surface->format->BytesPerPixel;
    if (nOfColors == 4)     // contains an alpha channel
    {
      if (surface->format->Rmask == 0x000000ff)
        texture_format = GL_RGBA;
      else
        texture_format = GL_BGRA;
    } else if (nOfColors == 3)     // no alpha channel
    {
      if (surface->format->Rmask == 0x000000ff)
        texture_format = GL_RGB;
      else
        texture_format = GL_BGR;
    } else {
      printf("warning: the image is not truecolor..  this will probably break\n");
      // this error should not go unhandled
    }

    // Bind the texture object
    glBindTexture( GL_TEXTURE_2D, texName[2] );

    // Set the texture's stretching properties
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    // Edit the texture object's image data using the information SDL_Surface gives us
    for(int i=0; i< (256*128*3); i++)
    {
      //((GLubyte*) surface->pixels)[i] += 20;
    }

    glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, surface->w, surface->h, 0,
      texture_format, GL_UNSIGNED_BYTE, surface->pixels );
  } 
  else {
    printf("SDL could not load image.bmp: %s\n", SDL_GetError());
    SDL_Quit();
    exit(0);
  }    

  // Free the SDL_Surface only if it was successfully created
  if ( surface ) { 
    SDL_FreeSurface( surface );
  }              


  // Fog
#if 0 // fog
  GLfloat density = 0.05;
  GLfloat fogColor[4] = {0.7, 0.7, 0.5, 1.0};
  glEnable (GL_DEPTH_TEST); //enable the depth testing
  glEnable (GL_FOG); //enable the fog
  glFogi (GL_FOG_MODE, GL_EXP2); //set the fog mode to GL_EXP2
  glFogfv (GL_FOG_COLOR, fogColor); //set the fog color to our color chosen above
  glFogf (GL_FOG_DENSITY, density); //set the density to the value above
  glHint (GL_FOG_HINT, GL_NICEST);
#endif // fog
}

CMainWindow::CMainWindow(int iWidth, int iHeight, bool bFullScreen) 
  : m_hWindow(NULL), m_hDeviceContext(NULL), m_hGLContext(NULL), 
    m_bFullScreen(bFullScreen),
    completed_rows(0),
    mouse_x(300),
    mouse_y(300),
    x_rot_angle(55),
    y_rot_angle(0),
    z_rot_angle(0),
    shift(0),
    scale(SCALE),
    rot_tick_conn_valid(0),
    multi(0)
{
	RegisterWindowClass();

	RECT WindowRect;
	WindowRect.top = WindowRect.left = 0;
	WindowRect.right = iWidth;
	WindowRect.bottom = iHeight;

	// Window Extended Style
	DWORD dwExStyle = 0;	
	// Windows Style
	DWORD dwStyle = 0;		

	if (m_bFullScreen)
	{
		DEVMODE dmScreenSettings;
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	
		dmScreenSettings.dmSize = sizeof(dmScreenSettings);		
		dmScreenSettings.dmPelsWidth	= iWidth;			
		dmScreenSettings.dmPelsHeight	= iHeight;		
		dmScreenSettings.dmBitsPerPel	= 32;		
		dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;

		// Change the display settings to fullscreen. On error, throw 
		// an exception.
		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)
				!= DISP_CHANGE_SUCCESSFUL)
		{
			throw CException("Unable to swith to fullscreen mode");
		}

		dwExStyle = WS_EX_APPWINDOW;	
		dwStyle = WS_POPUP;		
		// In fullscreen mode, we hide the cursor.
		ShowCursor(FALSE);
	}
	else
	{
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle = WS_OVERLAPPEDWINDOW;	
	}

	// Adjust the window to the true requested size
	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);		
	// Now create the main window
    m_hWindow = CreateWindowEx(dwExStyle,TEXT(WINDOW_CLASSNAME), 
							 TEXT("Worm Roaster"), 
							 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dwStyle,
							 0, 0, WindowRect.right-WindowRect.left, 
							 WindowRect.bottom-WindowRect.top, 
							 NULL, NULL, 
							 GetModuleHandle(NULL), 
							 this);
	if (m_hWindow==NULL)
		throw CException("Cannot create the main window");

	CreateContext();
	InitGL();
	ShowWindow(m_hWindow,SW_SHOW);
	// Call OnSize manually because in fullscreen mode it will be 
	// called only when the window is created (which is too early
	// because OpenGL is not initialized yet).
	OnSize(iWidth,iHeight);

  init();

}

CMainWindow::~CMainWindow()
{
	if (m_bFullScreen)
	{
		// Remove the full screen setting
		ChangeDisplaySettings(NULL,0);
		ShowCursor(TRUE);	
	}

	if (m_hGLContext)
	{
		// Make the rendering context not current
		wglMakeCurrent(NULL,NULL);
		// Delete the OpenGL rendering context
		wglDeleteContext(m_hGLContext);
		m_hGLContext = NULL;
	}
	if (m_hDeviceContext)
	{
		// Release the device context
		ReleaseDC(m_hWindow,m_hDeviceContext);
		m_hDeviceContext = NULL;
	}

	// Finally, destroy our main window and unregister the
	// window class.
    DestroyWindow(m_hWindow);
    UnregisterClass(TEXT(WINDOW_CLASSNAME), GetModuleHandle(NULL));
}

LRESULT CMainWindow::OnEvent(HWND Handle, UINT Message, WPARAM wParam, LPARAM lParam)
{
  if (Message == WM_NCCREATE)
  {
    // Get the creation parameters.
    CREATESTRUCT* pCreateStruct = reinterpret_cast<CREATESTRUCT*>(lParam);

    // Set as the "user data" parameter of the window
    SetWindowLongPtr(Handle, GWLP_USERDATA, 
      reinterpret_cast<long>(pCreateStruct->lpCreateParams));
  }

  // Get the CMainWindow instance corresponding to the window handle
  CMainWindow* pWindow = reinterpret_cast<CMainWindow*>
    (GetWindowLongPtr(Handle, GWLP_USERDATA));
  if (pWindow)
    pWindow->ProcessEvent(Handle, Message,wParam,lParam);

  return DefWindowProc(Handle, Message, wParam, lParam);
}

void CMainWindow::ProcessEvent(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
  switch (Message)
  {  
  case WM_CLOSE : // Quit when we close the main window
    PostQuitMessage(0);
    break;
  case WM_SIZE:
    OnSize(LOWORD(lParam),HIWORD(lParam));
    break;
  case WM_KEYDOWN :
    break;
  case WM_CHAR : 
    switch (wParam) 
    { 
    case 'q':
      exit(0);
      break;
    }
    break;
  case WM_KEYUP :
    break;
  case WM_MOUSEMOVE :
    POINT pos;
    GetCursorPos(&pos);
    ScreenToClient(hwnd,&pos);
    std::ostringstream ss;
    ss << "(" << pos.x << ", " << pos.y << ")" << endl;
    //OutputDebugString(ss.str().c_str());
    mouse_x = pos.x;
    mouse_y = pos.y;
    break;
  }
}

void CMainWindow::RegisterWindowClass()
{
    WNDCLASS WindowClass;
    WindowClass.style         = 0;
    WindowClass.lpfnWndProc   = &CMainWindow::OnEvent;
    WindowClass.cbClsExtra    = 0;
    WindowClass.cbWndExtra    = 0;
    WindowClass.hInstance     = GetModuleHandle(NULL);
    WindowClass.hIcon         = NULL;
    WindowClass.hCursor       = 0;
    WindowClass.hbrBackground = 0;
    WindowClass.lpszMenuName  = NULL;
    WindowClass.lpszClassName = WINDOW_CLASSNAME;

    RegisterClass(&WindowClass);
}

void CMainWindow::CreateContext()
{
  std::cout << "test" << std::endl;
	// Describes the pixel format of the drawing surface
	PIXELFORMATDESCRIPTOR pfd;
	memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	pfd.nVersion = 1;	// Version Number
	pfd.dwFlags = PFD_DRAW_TO_WINDOW |	// Draws to a window
				  PFD_SUPPORT_OPENGL |	// The format must support OpenGL
				  PFD_DOUBLEBUFFER;		// Support for double buffering
	pfd.iPixelType = PFD_TYPE_RGBA;		// Uses an RGBA pixel format
	pfd.cColorBits = 32;				// 32 bits colors

	if (!(m_hDeviceContext=GetDC(m_hWindow)))	
		throw CException("Unable to create rendering context");

	int PixelFormat;
	// Do Windows find a matching pixel format ?
	if (!(PixelFormat=ChoosePixelFormat(m_hDeviceContext,&pfd)))				
		throw CException("Unable to create rendering context");
	// Set the new pixel format
	if(!SetPixelFormat(m_hDeviceContext,PixelFormat,&pfd))			
		throw CException("Unable to create rendering context");
	// Create the OpenGL rendering context
	if (!(m_hGLContext=wglCreateContext(m_hDeviceContext)))	
		throw CException("Unable to create rendering context");
	// Activate the rendering context
	if(!wglMakeCurrent(m_hDeviceContext,m_hGLContext))
		throw CException("Unable to create rendering context");						
}

void CMainWindow::InitGL()
{
    std::cout << "test" << std::endl;
	// Enable 2D texturing
	glEnable(GL_TEXTURE_2D);
	// Choose a smooth shading model
    glShadeModel(GL_SMOOTH);
	// Set the clear color to black
	glClearColor(0.0, 0.0, 0.0, 0.0);

	// Enable the alpha test. This is needed 
	// to be able to have images with transparent 
	// parts.
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 0.0f);



    // Just enable depth testing and set the background colour.
  glEnable(GL_DEPTH_TEST);
  #if 1
  glEnable (GL_BLEND);
  
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

//  glClearColor(0.7, 0.7, 1.0, 0.0);
  #if 1
   GLfloat mat_ambient[] = { 0.5, 0.5, 0.5, 1.0 };
   GLfloat mat_specular[] = { 0, 0, 0, 0 };
   GLfloat mat_shininess[] = { 5.0 };

   GLfloat model_ambient[] = { 0.7, 0.7, 0.7, 1.0 };

   glClearColor(0.0, 0.0, 0.0, 0.0);


   glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, model_ambient);

   glEnable(GL_LIGHTING);
   glEnable(GL_DEPTH_TEST);
    glShadeModel (GL_SMOOTH);
   glEnable ( GL_COLOR_MATERIAL ) ;



  float LightDir[3] = {0.0f, -1, 0}; // towards the viewer
  GLfloat color[4] = { 2.0, 1.0, 1.0, 1.0 };

   glEnable(GL_LIGHT1);
   glLighti(GL_LIGHT1, GL_SPOT_CUTOFF, 30); 

	glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, LightDir);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, color);
    glLightfv(GL_LIGHT1, GL_SPECULAR, color);
      glLightfv(GL_LIGHT1, GL_AMBIENT, color);
//  glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
#endif
#endif
}

void CMainWindow::OnSize(GLsizei width, GLsizei height)
{
	// Sets the size of the OpenGL viewport
  glViewport(0,0,width,height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(40.0, (GLfloat)width/(GLfloat)height, 0.1, 1000.0);
  // Reset to modelview matrix mode

  glMatrixMode(GL_MODELVIEW);
}

void CMainWindow::Update(DWORD dwCurrentTime)
{
  tick();
}

void CMainWindow::drawCube(double x, double y, double z)
{
  x -= 0.5;
  z -= 0.5;
  glBegin(GL_QUADS);
  if (multi) glColor3d(multi_colours[0][0],multi_colours[0][1], multi_colours[0][2]);
  glVertex3d(x, y, z);
  glVertex3d(x+1, y, z);
  glVertex3d(x+1, y+1, z);
  glVertex3d(x, y+1, z);
  
  if (multi) glColor3d(multi_colours[1][0],multi_colours[1][1], multi_colours[1][2]);  
  glVertex3d(x, y, z+1);
  glVertex3d(x+1, y, z+1);
  glVertex3d(x+1, y+1, z+1);
  glVertex3d(x, y+1, z+1);
  
  if (multi) glColor3d(multi_colours[2][0],multi_colours[2][1], multi_colours[2][2]);
  glVertex3d(x, y+1, z);
  glVertex3d(x+1, y+1, z);
  glVertex3d(x+1, y+1, z+1);
  glVertex3d(x, y+1, z+1);
  
  if (multi) glColor3d(multi_colours[3][0],multi_colours[3][1], multi_colours[3][2]);
  glVertex3d(x, y, z);
  glVertex3d(x+1, y, z);
  glVertex3d(x+1, y, z+1);
  glVertex3d(x, y, z+1);
  
  if (multi) glColor3d(multi_colours[4][0],multi_colours[4][1], multi_colours[4][2]);
  glVertex3d(x, y, z);
  glVertex3d(x, y, z+1);
  glVertex3d(x, y+1, z+1);
  glVertex3d(x, y+1, z);
  
  if (multi) glColor3d(multi_colours[5][0],multi_colours[5][1], multi_colours[5][2]);
  glVertex3d(x+1, y, z);
  glVertex3d(x+1, y, z+1);
  glVertex3d(x+1, y+1, z+1);
  glVertex3d(x+1, y+1, z);
  glEnd();
}

void CMainWindow::drawBorder()
{
  glColor3d(0.3,0.3,0.3);
  for(int y=-1;y<HEIGHT;y+=1) {
    drawCube(-1,y,0);
    drawCube(WIDTH,y,0);
  }
  for(int x=0;x<WIDTH;x+=1) {
    drawCube(x,-1,0);
  }
}

void CMainWindow::tick()
{
  eng.tick();
}

void CMainWindow::drawWorm(Point3D pos, Point3D base, Point3D MG_pos, double temp_t, game_engine *eng, int index)
{ 
  if (eng->worms[index].burning) {
    temp_t = eng->worms[index].temp_t;
  }
  
  Vector3D tobase = (base-pos);
  double tobase_angle = rad_to_deg(atan2(-tobase[2], tobase[0])) + 180;
  static double tilt = 10.0;
  static int dir = 1;
  glColor3d(80.0/255.0, 24.0/255.0, 0);
  GLUquadric *quad = gluNewQuadric();
  glPushMatrix();
  if (eng->worms[index].burning)
    glScaled(1, 1*eng->worms[index].frac(), 1);
  glTranslated(pos.x, pos.y, pos.z);
  glRotated(tobase_angle, 0, 1, 0);
  glScaled(1,1,1);
  Matrix4x4 trans;
  
  double distance = sin(deg_to_rad(2*temp_t));
  
  double ang = 90*0.5*(1+sin(deg_to_rad(2*temp_t)));
  double dist = 45-fabs(ang-45);
  double ls = dist/45;
  double sf = .2;
  
  double xxx = -2*sin(deg_to_rad(temp_t));
  trans = trans * translation(Vector3D(0, 0, 0)) * rotation(45 + ang, 'y') * translation(Vector3D(0, 0, 0));
  
  glPushMatrix();
  glPopMatrix();
  
  int circle_segments = 8;
  vector<Point3D> ring1, ring2; 
  ring1.resize(circle_segments);
  ring2.resize(circle_segments);
  
  vector<Point3D> *A = &ring1, *B = &ring2;
  for(int t=0; t<circle_segments; t++)
  {
    double deg = double(t)/circle_segments * 360;
    double cx = cos(deg_to_rad(deg));
    double cy = sin(deg_to_rad(deg));
    A->at(t) = trans * Point3D(cx, cy, 0);
  }

  Vector3D normal = (A->at(1) -A->at(0)).cross(A->at(2) -A->at(1));
  glNormal3d(normal[0], normal[1], normal[2]);
  glBegin(GL_POLYGON);        for (int p=0; p< eng->ps.size ; p++)
  for(int i=0; i<A->size(); i++)
  {
    Point3D p = A->at(i);
    glVertex3d(p.x, p.y, p.z);
  }
  glEnd();
  int dir2 = 1;
  for(int r=0; r<7; r++)
  {
    bool red = false;
    double ang2 = ang-45;
    if (ang2 < 0) {
      tilt = 45*(fabs(ang2)/45);
      dir2 = -1;
    }
    else {
      tilt = 45*(fabs(ang2)/45);
      dir2 = 1;
    }
      
    if (r == 2 || r == 5) dir2 *= 1;
    else if (r == 8 || r == 11) dir2 *= -1;
    
    bool istilt = false;
    if ((r%3) == 2 && 1) {
      double sign = (dir2 < 0) ? -1 : 1;
      istilt = true;
      trans = trans * translation(Vector3D(0, 0, 0)) * translation(Vector3D(sign*-1, 0, 0)) *
                      rotation(-dir2*tilt, 'y') * translation(Vector3D(sign*1, 0, 0));
      dir2 *= (-1);
      tilt += dir;
      if (tilt > 45) dir *= -1;
      else if (tilt < 0) dir *= -1;
      
    } else {
      trans = trans * translation(Vector3D(0, 0, 1 + sf*ls));
    }
    
    double margin = 0.5;
    for(int t=0; t<circle_segments; t++)
    {
      double deg = double(t)/circle_segments * 360;
      double cx = cos(deg_to_rad(deg));
      double cy = sin(deg_to_rad(deg));
      B->at(t) = trans * Point3D(cx, cy, 0);
      if (!red && t == 3) {
        Point3D apos = ( rotation(tobase_angle, 'y')*B->at(t) ) + (pos -Point3D(0,0,0));
        if (fabs(apos.x-MG_pos.x) < margin && fabs(apos.z-MG_pos.z) < margin) {
          red = true;
          eng->burnt(index, temp_t);
        }
      }
    }
    
    if (r == 6)
    {
      glBegin(GL_POLYGON);
      for(int i=0; i<ring1.size(); i++)
      {
        Point3D p = B->at(i);
        glVertex3d(p.x, p.y, p.z);
      }
      glEnd();
    }
    
    #if 1
    for(int i=0; i<circle_segments; i++)
    {
      Point3D pt1a = A->at(i);
      Point3D pt1b = A->at((i+1)%ring1.size());
      Point3D pt2a = B->at(i);
      Point3D pt2b = B->at((i+1)%ring2.size());      
      
      Vector3D normal = (pt1b -pt1a).cross(pt2b -pt1a);
      glNormal3d(normal[0], normal[1], normal[2]);  
      normal.normalize();
      GLUquadric *quad = gluNewQuadric();
      if (eng->worms[index].burning && (i==1 || i==2))
      {
        /*************************
         *     Fire
         *************************/
        Vector3D leg1 = pt2a-pt1a;
        Vector3D leg2 = pt1b-pt1a;

        glEnable (GL_BLEND);
        glBlendFunc (GL_SRC_ALPHA, GL_ONE);
        for (int p=0; p<eng->ps.size; p++)
        {
          double size = 0.1;
          Point3D pos = eng->ps.p[p].pos;
          glColor3d(eng->worms[index].frac()* 1.0 * 0.5*(pow(1*eng->ps.p[p].frac(), 1) + unif()*0.0 + pow(2, -10*fabs(pos.x-0.5)) ),
                    eng->worms[index].frac() * 0.5*pow(( 0.5*0.5*( pow(2.0, -10*fabs(pos.x-0.5)) + pow(2.0, -10*fabs(pos.y-0.5)) )+ 
                              eng->worms[index].frac()*pow(1*eng->ps.p[p].frac(),2) +
                              eng->worms[index].frac()*unif()*0.0 ), 5),
                    0.0);
          pos = pt1a + pos.z*leg2 + pos.x*leg1 + (1 + (1-eng->worms[index].frac())*2)*pos.y*normal;
          if (1)
          {
            glBegin(GL_QUADS);
            glVertex3d(pos.x, pos.y, pos.z);
            glVertex3d(pos.x, pos.y+size, pos.z);
            glVertex3d(pos.x+size, pos.y+size, pos.z);
            glVertex3d(pos.x+size, pos.y, pos.z);      
            glEnd();
          }
          else
          {
            glPushMatrix();
            glTranslated(pos.x, pos.y, pos.z);
            glRotated(x_rot_angle, 1,0,0);
            //glRotated(-y_rot_angle, 0,1,0);
            //glRotated(-z_rot_angle, 0,0,1);

            glBegin(GL_QUADS);
            glVertex3d(0, 0, 0);
            glVertex3d(0+size, 0, 0);
            glVertex3d(0+size, 0, 0+size);
            glVertex3d(0, 0, 0+size);
            glEnd();
            glPopMatrix();
          }
        }
        glDepthMask (GL_TRUE);
        glDisable (GL_BLEND);
        glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
      }
      
      if (eng->worms[index].burning) {
        glColor3d(2*80.0/255.0, 2*24.0/255.0, 0);
      } else {
        glColor3d(2*80.0/255.0, 2*24.0/255.0, 0);
      }

      //glEnable(GL_TEXTURE_2D);
      //   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
      //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

      //glBindTexture(GL_TEXTURE_2D, texName[2]);

      double bark_s = 0.1;          
      glBegin(GL_POLYGON);
      //glTexCoord2d (r*bark_s, r*bark_s);
      glVertex3d(pt1a.x, pt1a.y, pt1a.z);
      //glTexCoord2d (r*bark_s, r*bark_s + bark_s);
      glVertex3d(pt2a.x, pt2a.y, pt2a.z);
      //glTexCoord2d (r*bark_s + bark_s/2, bark_s);
      glVertex3d(pt2b.x, pt2b.y, pt2b.z);
      //glTexCoord2d (r*bark_s + bark_s/2, r*bark_s); 
      glVertex3d(pt1b.x, pt1b.y, pt1b.z);                  
      glEnd();
      
    glDisable(GL_TEXTURE_2D);
    }
    #endif
    vector<Point3D> *tmp = A;
    A = B;
    B = tmp;
  }
  
  glPopMatrix();
}


void drawRing(int sten)
{
  int segments = 20;
  double height_scale = 0.5;
  
  vector< vector<double> > x_vals;
  vector< vector<double> > y_vals;
  
  x_vals.resize(segments+1);
  y_vals.resize(segments+1);
  
  for(int s = 0; s <= segments ; s++)
  {
    x_vals.at(s).resize(2);
    y_vals.at(s).resize(2);
    
    double d = (double(s)/double(segments)) * 360.0;
    double r_outer = 1, r_inner = 0.9;
    double x_outer = r_outer*cos(deg_to_rad(d)), y_outer = r_outer*sin(deg_to_rad(d));
    double x_inner = r_inner*cos(deg_to_rad(d)), y_inner = r_inner*sin(deg_to_rad(d));
    x_vals.at(s)[0] = x_inner;
    y_vals.at(s)[0] = y_inner;
    
    x_vals.at(s)[1] = x_outer;
    y_vals.at(s)[1] = y_outer;
  }
  
  glBegin(GL_QUADS);
  for(int i=0; i<=segments; i++)
  {
    int next = (i+1)%(segments+1);
   
    glColor3d(0.5, 0.5, 0.5);
    glVertex3d(x_vals.at(i)[0], 0, y_vals.at(i)[0]);
    glVertex3d(x_vals.at(i)[1], 0, y_vals.at(i)[1]);
    glVertex3d(x_vals.at(next)[1], 0, y_vals.at(next)[1]);    
    glVertex3d(x_vals.at(next)[0], 0, y_vals.at(next)[0]);

    glColor3d(0.5, 0.5, 0.5);
    glVertex3d(x_vals.at(i)[0], height_scale, y_vals.at(i)[0]);
    glVertex3d(x_vals.at(i)[1], height_scale, y_vals.at(i)[1]);
    glVertex3d(x_vals.at(next)[1], height_scale, y_vals.at(next)[1]);    
    glVertex3d(x_vals.at(next)[0], height_scale, y_vals.at(next)[0]);
    
    glColor3d(0.3, 0.3, 0.3);    
    glVertex3d(x_vals.at(i)[0], 0, y_vals.at(i)[0]);
    glVertex3d(x_vals.at(i)[0], height_scale, y_vals.at(i)[0]);
    glVertex3d(x_vals.at(next)[0], height_scale, y_vals.at(next)[0]);
    glVertex3d(x_vals.at(next)[0], 0, y_vals.at(next)[0]);
    
    glColor3d(0.5, 0.5, 0.5);
    glVertex3d(x_vals.at(i)[1], 0, y_vals.at(i)[1]);
    glVertex3d(x_vals.at(i)[1], height_scale, y_vals.at(i)[1]);
    glVertex3d(x_vals.at(next)[1], height_scale, y_vals.at(next)[1]);
    glVertex3d(x_vals.at(next)[1], 0, y_vals.at(next)[1]);
  }
  glEnd();
  
  GLfloat mat_solid[] = { 0.75, 0.75, 0.0, 1.0 };
  GLfloat mat_zero[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat mat_transparent[] = { 0.0, 0.0, 0.9, 0.0 };
  GLfloat mat_emission[] = { 0.0, 0.3, 0.3, 0.6 };
  
//  glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
//  glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_transparent);
  glEnable (GL_BLEND);
  glDepthMask (GL_FALSE);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE);
  
  if (sten == 0) {
    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
    glStencilFunc(GL_ALWAYS, 0, 1);
  } else if (sten == -1) {
    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);    
    glStencilFunc(GL_ALWAYS, 0, 1);
  }

  {    
    glColor3d(0.3, 0.3, 0.4);
    glBegin(GL_POLYGON);
    for(int i=0; i<=segments; i++)
    { 
      glVertex3d(x_vals.at(i)[1], 0, y_vals.at(i)[1]);
    }
    glEnd();
  }
  
  if (sten == 0) {
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glStencilFunc(GL_EQUAL, 0, 1);
  } else if (sten == -1) {
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);    
    glStencilFunc(GL_EQUAL, 0, 1);
  }
  
  glDepthMask (GL_TRUE);
  glDisable (GL_BLEND);
}

void CMainWindow::drawMG(int s_sten)
{
  glPushMatrix();
  GLUquadric *quad = gluNewQuadric();

  double handle_rad = 1.0;
  double handle_length = handle_rad * 10;
  double rivet_scale = 7.0;
  double rivet_rad_scale = 1.2;
  glColor3d(0.1, 0.1, 0.1);
  g_x = max(0.0, double(min(win_size, mouse_x)))/double(win_size);
  g_y = (600.0 -double(max(0.0, (double)min(win_size, mouse_y))) )/double(win_size);
  
  g_x -= 0.5;
  g_y -= 0.5;
  
  g_x *= 8*eng.ground.width;
  g_y *= 8*eng.ground.length;
  
  glTranslated(g_x, 5.0, -(g_y-handle_length*3/2));
  glRotated(180, 0,1,0);
  
  gluCylinder(quad, handle_rad, handle_rad, handle_length, 10, 10);
  gluDisk(quad, 0, handle_rad, 10, 10);
  
  glPushMatrix();
  glTranslated(0, 0, handle_length - handle_length/rivet_scale);
  glColor3d(0.6, 0.6, 0.6);
  gluDisk(quad, handle_rad, handle_rad*rivet_rad_scale, 10, 10);
  gluCylinder(quad, handle_rad*rivet_rad_scale, handle_rad*rivet_rad_scale, handle_length/rivet_scale, 10, 10);
  glTranslated(0, 0, handle_length/rivet_scale);
  gluDisk(quad, 0, handle_rad*rivet_rad_scale, 10, 10);
  glPopMatrix();
  
  eng.MG_pos = Point3D(g_x, 5.0, -g_y);
  
  glTranslated(0, -2*rivet_rad_scale*1.2/2/2, handle_length*(1.45));
  glScaled(handle_length/2, 2*rivet_rad_scale*1.2, handle_length/2);
  drawRing(s_sten);
  glPopMatrix();  
}


static int temp_f;

bool CMainWindow::draw_it()
{
  GLUquadric *quad = gluNewQuadric();

for (int render=0; render<2; render++) {
  sten = render;
  if (sten == 0)
  {
    // Clear the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glClearStencil(1);

//    glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
//    glDisable(GL_STENCIL_TEST);
    glEnable(GL_STENCIL_TEST);
    glStencilFunc(GL_ALWAYS, 1, 1);
    glStencilFunc(GL_NOTEQUAL, 1, 1);
//    glColorMask(0,0,0,0);
    glEnable(GL_DEPTH_TEST);
//    glDisable(GL_DEPTH_TEST);

    glPushMatrix();
    glScaled(3,3,3);
    glTranslated(eng.MG_pos.x, eng.MG_pos.y, eng.MG_pos.z);
    gluCylinder(quad, 1, 1, 1, 10, 10);
    glPopMatrix();
    
//    glStencilFunc(GL_ALWAYS, 0, 0);
    
    glPushMatrix();
    glScaled(3,3,3);
    glTranslated(eng.MG_pos.x, eng.MG_pos.y, eng.MG_pos.z);
    gluCylinder(quad, 1, 1, 1, 10, 10);
    glPopMatrix();
    //continue;
  }
  else
  {
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
    glStencilFunc(GL_NOTEQUAL, 0, 1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_DEPTH_TEST);
  }
  
  // Modify the current projection matrix so that we move the 
  // camera away from the origin.  We'll draw the game at the
  // origin, and we need to back up to see it.

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glTranslated(0.0, 0.0, -40.0);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

  // Not implemented: scale and rotate the scene
  glRotated(x_rot_angle, 1,0,0);
  glRotated(y_rot_angle, 0,1,0);
  glRotated(z_rot_angle, 0,0,1);
  glScaled((double)scale/(double)SCALE,
           (double)scale/(double)SCALE,
           (double)scale/(double)SCALE);

  // look at the ground from above-back
  glTranslated(0.0, -12.0, 0.0);
//  glTranslated(0.0, -8.0, -10.0);
  
  if (sten==0) {
    drawMG(-1);
    glPushMatrix();
    glScaled(1.2, 1.2, 1.2);
  } else {
  }
  
  glPushMatrix();
    glColor3d(1,1,1);
    glScaled(1,1,1);
    glTranslated(eng.MG_pos.x, eng.MG_pos.y, eng.MG_pos.z);
  glPopMatrix();

  GLfloat light_position[] = { 0, 30.0, 0, 1};
  GLfloat light0_dc[] = { 1, 1, 1, 1};
  glEnable(GL_LIGHT0);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  glLightfv (GL_LIGHT0, GL_DIFFUSE, light0_dc);
  
  float LightDir[3] = {0.0f, -1, 0};
  GLfloat color[4] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat position[4] ={ g_x, 80.0, -g_y, 1};

  glEnable(GL_LIGHT1);
  glLighti(GL_LIGHT1, GL_SPOT_CUTOFF, 1);
	glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, LightDir);
	glLightfv(GL_LIGHT1, GL_POSITION, position);
  
#if 0
            Point3D pos(eng.base.pos.x,eng.base.pos.y+0.1, eng.base.pos.z);
            double size = 2;
            glPushMatrix();
            glTranslated(pos.x, pos.y, pos.z);
            glRotated(x_rot_angle, 1,0,0);
            //glRotated(20, 1,0,0);
            //glRotated(y_rot_angle, 0,1,0);
            //glRotated(z_rot_angle, 0,0,1);
            //glColor3d(0,0,1);
            glBegin(GL_QUADS);
            glVertex3d(0, 0, 0);
            glVertex3d(0+size, 0, 0);
            glVertex3d(0+size, 0, 0+size);
            glVertex3d(0, 0, 0+size);
            glEnd();
            glPopMatrix();
#endif
  
  // draw ground
  
   glEnable(GL_TEXTURE_2D);
//   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   glBindTexture(GL_TEXTURE_2D, texName[0]);
  
  glNormal3d(0, 1, 0);
  
  {
    double gr_f = 1;
    int res = 100;
    for(int i=0; i<res; i++) {
      for(int j=0; j<res; j++) {
        glBegin(GL_QUADS);
        
        double gr_w = eng.ground.width * gr_f;
        double gr_l = eng.ground.length * gr_f;
        
        double l_x = -gr_w+(double(i)/res)*2.0*gr_w;
        double r_x = l_x + (2.0*gr_w*1.0/res);
        
        double b_y = -gr_l+(double(j)/res)*2.0*gr_l;
        double t_y = b_y + (2.0*gr_w*1.0/res);
        
        
        double tl_x = (double(i)/res);
        double tr_x = (double(i+1)/res);
        
        double tb_y = (double(j)/res);
        double tt_y = (double(j+1)/res);
        
        
        glTexCoord2d (tl_x, tb_y);
        glVertex3d(l_x, 0, b_y);
                   
        glTexCoord2d (tr_x, tb_y);
        glVertex3d(r_x, 0, b_y);

        glTexCoord2d (tr_x, tt_y); glVertex3d(r_x, 0, t_y);  
        glTexCoord2d (tl_x, tt_y); glVertex3d(l_x, 0, t_y);
        glEnd();
      }
    }
  }

  glDisable(GL_TEXTURE_2D);

  // draw base
  glColor3d(0.5, 0.5, 0.5);
  double temp_f_d = abs(temp_f)/(double)INT_MAX;
  glColor3d(temp_f_d, temp_f_d, temp_f_d);
  temp_f++;
  drawCube(eng.base.pos.x,eng.base.pos.y, eng.base.pos.z);
  glPushMatrix();    
  glTranslated(eng.base.pos.x,eng.base.pos.y, eng.base.pos.z);

  if (eng.base.hit) {
    glPushMatrix();
    glScaled(2,2,2);
    glEnable (GL_BLEND);
    glDepthMask (GL_FALSE);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE);
    int num_sparks = int( double(eng.base.hit)/double(eng.base.H) * 1000) ;
    glColor3d(0.5, 0.5, 0.9);
    for(int spark=0; spark<num_sparks; spark++)
    {
      double size = 0.1;
      double ro = unif() * 2 * M_PI;
      double theta = unif() * M_PI;
      Point3D pos(sin(theta)*cos(ro), sin(theta)*sin(ro), cos(theta));
      Vector3D v1 = Vector3D(0,0,1).cross( pos-Point3D(0,0,0));
      Vector3D v2 = Vector3D(0,1,0).cross( pos-Point3D(0,0,0));
      v1.normalize();
      v2.normalize();
      Point3D v1p = pos + size*v1;
      Point3D v2p = pos + size*v2;
      glBegin(GL_TRIANGLES);
      glVertex3d(pos.x, pos.y, pos.z);
      glVertex3d(v1p.x, v1p.y, v1p.z);
      glVertex3d(v2p.x, v2p.y, v2p.z);
      glEnd();
    }
    glPopMatrix();
    glDepthMask (GL_TRUE);
    glDisable (GL_BLEND);
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
  }

  glTranslated(0.0, 1, 0.0);
  glColor3d(0.5, 0.5, 0.9);
  gluSphere(quad, 0.5, 5, 5);
  glPopMatrix();
  
  // draw worms
  if (1)
  for(int i=0; i<eng.num_worms; i++)
  {
    glColor3d(0.2, 0.2, 0.2);
    drawWorm(eng.worms[i].pos, eng.base.pos, eng.MG_pos, eng.temp_t*eng.worms[i].wiggle_speed_factor, &eng, i);
    glPushMatrix();
    glTranslated(eng.worms[i].pos.x, eng.worms[i].pos.y+1, eng.worms[i].pos.z);
    glColor3d(1.0, 0.0, 0.0);
    glPopMatrix();
  }
  
  if (sten == 0) {
    glPopMatrix();
  }
  
  drawMG(sten);
  
  // We pushed a matrix onto the PROJECTION stack earlier, we 
  // need to pop it.

  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  
} // sten 
  
  return true;
}

void CMainWindow::Draw()
{  
  draw_it();
	SwapBuffers(m_hDeviceContext);
}
